﻿namespace EFS.RC.CC.Confereces
{
	/// <summary>
	/// Customer to office conference
	/// </summary>
	class OfficeService : EFS.Common.Async.AsyncRoot
	{
		#region Composition

		// sync
		private object syncAccess = new object();

		// Application end point
		private EFS.RC.CC.Sessions.OfficeService.Customers.Customer customerSession = null;

		// Agent
		private System.Collections.Generic.List<EFS.RC.CC.Sessions.OfficeService.Operators.Operator> operatorSessions = new System.Collections.Generic.List<EFS.RC.CC.Sessions.OfficeService.Operators.Operator>();

		// monitoring session
		private EFS.RC.CC.Sessions.OfficeService.Operators.Supervisors.Monitoring supervisorMonitoringSession = null;

		// step by step session
		private EFS.RC.CC.Tools.AVR.AStepByStep stepByStepAVRSession = null;

		// Conference
		private Microsoft.Rtc.Collaboration.Conference conference = null;

		// Conversation
		private Microsoft.Rtc.Collaboration.Conversation conversation = null;

		// recorder
		private EFS.RC.CC.Tools.Recorders.Recorder recorder = null;

		// Response group
		private System.Collections.Generic.List<EFS.RC.CC.Tools.Hunters.Group> responseGroups = new System.Collections.Generic.List<EFS.RC.CC.Tools.Hunters.Group>();

		/// <summary>
		/// Status
		/// </summary>
		public enum OfficeServiceStatus { Idle, Started, Terminated }

		// Status
		private OfficeServiceStatus status = OfficeServiceStatus.Idle;

		#endregion

		#region Delegates

		/// <summary>
		/// Exec safe
		/// </summary>
		/// <param name="agentHunter"></param>
		/// <returns></returns>
		public delegate void ExecSafe();

		/// <summary>
		/// Asynk established 
		/// </summary>
		public EFS.Common.Async.AsyncDelegate<EFS.RC.CC.Confereces.OfficeService> AsinkEstablishedDelegate { get; private set; }

		/// <summary>
		/// Asynk terminate 
		/// </summary>
		public EFS.Common.Async.AsyncDelegate<EFS.RC.CC.Confereces.OfficeService> AsinkTerminatedDelegate { get; private set; }

		/// <summary>
		/// Called when transfer had failed
		/// </summary>
		/// <returns></returns>
		public delegate void TransferFailed(System.Exception ex);

		/// <summary>
		/// Called when dial had failed
		/// </summary>
		/// <returns></returns>
		public delegate void DialOutFailed(System.Exception ex);

		#endregion

		#region Constructors

		/// <summary>
		/// Constructor for conference
		/// </summary>
		/// <param name="customerSession"></param>
		internal OfficeService(EFS.RC.CC.Sessions.OfficeService.Customers.Customer customerSession)
		{
			// check
			EFS.Common.Verify.Reference(customerSession, "customerSession");

			// set
			this.customerSession = customerSession;

			// delegate
			AsinkEstablishedDelegate = new EFS.Common.Async.AsyncDelegate<OfficeService>(this);
			AsinkTerminatedDelegate = new EFS.Common.Async.AsyncDelegate<OfficeService>(this);

			// instance conversation
			conversation = new Microsoft.Rtc.Collaboration.Conversation(customerSession.CustomersOfficeServiceEndPointApplication.CollaborationApplicationEndpoint);
			conversation.ApplicationContext = this;
		}

		#endregion

		#region Usage

		// Conversation
		public Microsoft.Rtc.Collaboration.Conversation Conversation1
		{ get { return conversation; } }

		// Customer session
		public EFS.RC.CC.Sessions.OfficeService.Customers.Customer CustomerSession { get { return customerSession; } }

		// Conference Uri
		public System.String ConferenceUri
		{
			get
			{
				// lock
				lock (syncAccess)
				{
					// check
					if (null == conference)
					{
						throw new System.InvalidOperationException("Conference not started");
					}

					// get
					return conference.ConferenceUri;
				}
			}
		}

		// Status
		public OfficeServiceStatus Status { get { return status; } }

		/// <summary>
		/// Describe this object into a nice formated string
		/// </summary>
		/// <returns></returns>
		public override System.String Describe()
		{
			return System.String.Format("EFS.RC.CC.Confereces.OfficeService [{0}]", customerSession.Describe());
		}

		/// <summary>
		/// Begin startup
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginStartup()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginStartup", this);

			// start
			return BeginAsyncProcess2(Startup, StartupComplete);
		}

		/// <summary>
		/// Begin shutdown
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginShutdown()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginShutdown", this);

			// start
			return BeginAsyncProcess2(Shutdown, OnShutdownComplete);
		}

		/// <summary>
		/// Begin wrap
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginWrap()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginShutdown", this);

			// start
			return BeginAsyncProcess2(Wrap);
		}

		/// <summary>
		/// 
		/// </summary>
		public System.Boolean IsSupervised()
		{
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check
				return null != supervisorMonitoringSession;
			}
		}

		/// <summary>
		/// Begin pause
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginPauseRecorder()
		{
			// log
			EFS.EventLog.Log.LogInfo("PauseRecorder", this);

			// start
			return BeginAsyncProcess2(PauseRecorder);
		}

		/// <summary>
		/// Begin resume
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginResumeRecorder()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginResumeRecorder", this);

			// start
			return BeginAsyncProcess2(ResumeRecorder);
		}

		/// <summary>
		/// Start misic for hunting agent
		/// </summary>
		/// <returns></returns>
		public void BeginHoldCustomerConversation()
		{
			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());

			// start
			BeginAsyncProcess2(HoldCustomerConversation);
		}

		/// <summary>
		/// Start music for hunting agent
		/// </summary>
		/// <returns></returns>
		public void BeginResumeCustomerConversation()
		{
			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());

			// start
			BeginAsyncProcess2(ResumeCustomerConversation);
		}

		
		/// <summary>
		/// Start music for hunting agent
		/// </summary>
		/// <returns></returns>
		public void SendOperatorMessage(EFS.RC.CC.Messages.Message message)
		{
			// make list of operators
			System.Collections.Generic.List<EFS.RC.CC.Sessions.OfficeService.Operators.Operator> currentOperatorSessions = new System.Collections.Generic.List<EFS.RC.CC.Sessions.OfficeService.Operators.Operator>();

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// add 
				currentOperatorSessions.AddRange(operatorSessions);
			}

			// send message to all
			// get participants
			foreach (EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession in currentOperatorSessions)
			{
				try
				{
					// send message
					operatorSession.SendMessage(message);
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);
				}
			}
		}


		/// <summary>
		/// Set supervisor session
		/// </summary>
		public void SetStepByStepSession(EFS.RC.CC.Tools.AVR.AStepByStep stepByStepAVRSession)
		{
			// log
			EFS.EventLog.Log.LogInfo("SetStepByStepSession", this);

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != OfficeServiceStatus.Started)
				{
					// shut down
					throw new System.InvalidOperationException("Office Service Conference not Started");
				}

				// check existing
				if (null != this.stepByStepAVRSession)
				{
					// shut down
					throw new System.InvalidOperationException("StepByStep session already set");
				}

				try
				{
					// set
					this.stepByStepAVRSession = stepByStepAVRSession;
					this.stepByStepAVRSession.AsinkTerminatedDelegate.Register(OnStepByStepSessionTerminated);
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);

					// clean-up
					this.stepByStepAVRSession = null;

					// throw up
					throw new System.Exception("Could not set StepByStep Monitoring Session", ex);
				}
			}
		}

		/// <summary>
		/// Set supervisor session
		/// </summary>
		public void SetSupervisorSession(EFS.RC.CC.Sessions.OfficeService.Operators.Supervisors.Monitoring supervisorMonitoringSession)
		{
			// log
			EFS.EventLog.Log.LogInfo("SetSupervisorSession", this);

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != OfficeServiceStatus.Started)
				{
					// shut down
					throw new System.InvalidOperationException("Office Service Conference not Started");
				}

				// check existing
				if (null != this.supervisorMonitoringSession)
				{
					// shut down
					throw new System.InvalidOperationException("Supervisor session already set");
				}

				try
				{
					// set
					this.supervisorMonitoringSession = supervisorMonitoringSession;
					this.supervisorMonitoringSession.AsinkTerminatedDelegate.Register(OnSupervisorSessionTerminated);
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);

					// clean-up
					this.supervisorMonitoringSession = null;

					// throw up
					throw new System.Exception("Could not set Supervisor Monitoring Session", ex);
				}
			}
		}

		/// <summary>
		/// Set supervisor session
		/// </summary>
		public void SetOperatorSession(EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession)
		{
			// log
			EFS.EventLog.Log.LogInfo("SetOperatorSession", this);

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != OfficeServiceStatus.Started)
				{
					// shut down
					throw new System.InvalidOperationException("Office Service Conference is not Started");
				}

				// check existing
				if (operatorSessions.Contains(operatorSession))
				{
					// shut down
					throw new System.InvalidOperationException("Operator already set");
				}

				try
				{
					// set
					// register terminate delegate for operator session
					operatorSession.AsinkTerminatedDelegate.Register(OnOperatorSessionTerminated);

					// add 
					operatorSessions.Add(operatorSession);
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);

					// clean-up
					operatorSessions.Remove(operatorSession);

					// throw up
					throw new System.Exception("Could not set Operator Session", ex);
				}
			}
		}

		/// <summary>
		/// Call hunt group
		/// </summary>
		/// <param name="huntGroupInvite"></param>
		public void SetInviteHuntGroup(EFS.RC.CC.Messages.Communicator.Lync.Context.HuntGroupInvite huntGroupInvite, EFS.AD.User adUserInviting)
		{
			// lock local acess
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check started
				if (status != OfficeServiceStatus.Started)
				{
					// not started
					return;
				}

				try
				{
					// SAL
					EFS.RC.CC.Tools.Loggers.SAL.Add(customerSession.ConversationGuid, "SetInviteHuntGroup", huntGroupInvite.Sip, this);

					// close hount group
					StopResponseGroupInvite();

					// check if sip empty
					if (EFS.Common.Verify.String(huntGroupInvite.Sip))
					{
						// make group hunt
						EFS.RC.CC.Tools.Hunters.Group responseGroup = new EFS.RC.CC.Tools.Hunters.Group(CustomerSession, adUserInviting, huntGroupInvite.Name, huntGroupInvite.Sip);
						responseGroup.AsinkTerminatedDelegate.Register(InviteHuntGroupTerminated);

						// hunt group
						responseGroup.BeginStartup();

						// add
						responseGroups.Add(responseGroup);

						// hold customer
						customerSession.Hold();

						// start music
						StartOperatorsDialPlayer();

					}
					else
					{
						// hold customer
						customerSession.Resume();

						// start music
						StopOperatorsPlayers();
					}
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);
				}
			}
		}

		/// <summary>
		/// Stop all invite
		/// </summary>
		public void StopResponseGroupInvite()
		{
			// lock local acess
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// shut down all
				foreach (EFS.RC.CC.Tools.Hunters.Group responseGroup in responseGroups)
				{
					responseGroup.BeginShutdown();
				}
			}
		}

		/// <summary>
		/// Check participant supervisor
		/// </summary>
		/// <returns></returns>
		internal System.Boolean IsSupervisor(System.String participantUri)
		{
			// get participants
			return null != supervisorMonitoringSession && EFS.RC.Common.Utilities.SipUriCompare(supervisorMonitoringSession.ParticipantEndPointUri, participantUri);
		}

		/// <summary>
		/// Check participant recorder
		/// </summary>
		/// <returns></returns>
		internal System.Boolean IsRecorder(System.String participantUri)
		{
			// get participants
			return null != recorder && EFS.RC.Common.Utilities.SipUriCompare(recorder.InpersonatedUri, participantUri);
		}

		/// <summary>
		/// Check participant supervisor
		/// </summary>
		/// <returns></returns>
		internal System.Boolean IsCustomer(System.String participantUri)
		{
			// get participants
			return EFS.RC.Common.Utilities.SipUriCompare(customerSession.ParticipantEndPointUri, participantUri);
		}

		/// <summary>
		/// Check participant operator
		/// </summary>
		/// <returns></returns>
		internal void SendOperatorsTone(System.Int32 tone, System.Single volume)
		{
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// get participants
				foreach (EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession in operatorSessions)
				{
					try
					{
						operatorSession.SendTone(tone, volume);
					}
					catch (System.Exception ex)
					{
						EFS.EventLog.Log.LogException(ex, this);
					}
				}
			}
		}

		/// <summary>
		/// Check participant operator
		/// </summary>
		/// <returns></returns>
		internal void SendAVRTone(System.Int32 tone, System.Single volume)
		{
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				if (null != stepByStepAVRSession)
				{
					stepByStepAVRSession.HandleTone(tone, volume);
				}
			}
		}

		/// <summary>
		/// Check participant operator
		/// </summary>
		/// <returns></returns>
		internal System.Boolean IsOperator(System.String participantUri)
		{
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// get participants
				foreach (EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession in operatorSessions)
				{
					if (EFS.RC.Common.Utilities.SipUriCompare(operatorSession.ParticipantEndPointUri, participantUri))
					{
						return true;
					}
				}
			}

			// not
			return false;
		}

		/// <summary>
		/// Check participant supervisor
		/// </summary>
		/// <returns></returns>
		private EFS.RC.CC.Sessions.OfficeService.Operators.Operator GetOperatorSession(System.String participantUri)
		{
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// get participants
				foreach (EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession in operatorSessions)
				{
					if (EFS.RC.Common.Utilities.SipUriCompare(operatorSession.ParticipantEndPointUri, participantUri))
					{
						return operatorSession;
					}
				}
			}

			// not
			return null;
		}

		/// <summary>
		/// Get remote participants end points
		/// </summary>
		/// <returns></returns>
		internal System.Collections.Generic.List<Microsoft.Rtc.Collaboration.ParticipantEndpoint> GetRemoteParticipantEndpoints()
		{
			return new System.Collections.Generic.List<Microsoft.Rtc.Collaboration.ParticipantEndpoint>(conversation.ConferenceSession.AudioVideoMcuSession.GetRemoteParticipantEndpoints());
		}

		/// <summary>
		/// Get remote end point
		/// </summary>
		/// <returns></returns>
		internal Microsoft.Rtc.Collaboration.ParticipantEndpoint GetEndPoint(System.String participantUri)
		{
			// get participants
			System.Collections.Generic.List<Microsoft.Rtc.Collaboration.ParticipantEndpoint> listOfParticipantEndpoints = GetRemoteParticipantEndpoints();

			// find customer end point and decide routes
			foreach (Microsoft.Rtc.Collaboration.ParticipantEndpoint participantEndpoint in listOfParticipantEndpoints)
			{
				// not for visible participants
				if (EFS.RC.Common.Utilities.SipUriCompare(participantUri, participantEndpoint.Participant.Uri))
				{
					return participantEndpoint;
				}
			}

			// done
			return null;
		}

		/// <summary>
		/// Get remote end point
		/// </summary>
		/// <returns></returns>
		public Microsoft.Rtc.Collaboration.ParticipantEndpoint GetCustomerEndPoint()
		{
			// get by impersonated uri
			return GetEndPoint(customerSession.ParticipantEndPointUri);
		}

		/// <summary>
		/// Get status list for participants
		/// </summary>
		/// <returns></returns>
		public EFS.RC.CC.Messages.Communicator.Lync.Participant.StatusList GetParticipanthStatusListMessage(System.Boolean withSupervisor)
		{
			// status array
			System.Collections.Generic.List<EFS.RC.CC.Messages.Communicator.Lync.Participant.Status> participantsStatusList =
				new System.Collections.Generic.List<EFS.RC.CC.Messages.Communicator.Lync.Participant.Status>();

			// lock access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check agent
				foreach (EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession in operatorSessions)
				{
					// add agent
					EFS.RC.CC.Messages.Communicator.Lync.Participant.Status agentStatus = new EFS.RC.CC.Messages.Communicator.Lync.Participant.Status();
					agentStatus.Description = System.String.Format("{0} {1}",
						operatorSession.OperatorPresence.User.LastName,
						operatorSession.OperatorPresence.User.FirstName);
					agentStatus.Type = Messages.Communicator.Lync.Participant.Type.Agent;

					//add
					participantsStatusList.Add(agentStatus);
				}

				// check customer
				if (null != customerSession)
				{
					// add customer
					EFS.RC.CC.Messages.Communicator.Lync.Participant.Status customerStatus = new EFS.RC.CC.Messages.Communicator.Lync.Participant.Status();
					customerStatus.Description = customerSession.DisplayName;
					customerStatus.Type = Messages.Communicator.Lync.Participant.Type.Customer;

					//add
					participantsStatusList.Add(customerStatus);
				}
			}

			// make status list
			return new EFS.RC.CC.Messages.Communicator.Lync.Participant.StatusList(participantsStatusList.ToArray());
		}

		/// <summary>
		/// Get conversation info message
		/// </summary>
		/// <returns></returns>
		public EFS.RC.CC.Messages.Communicator.Lync.Conversation.Info GetConversationInfoMessage()
		{
			// info message
			EFS.RC.CC.Messages.Communicator.Lync.Conversation.Info conversationInfo = new Messages.Communicator.Lync.Conversation.Info();

			// add agent
			conversationInfo.IsMusicOn = customerSession.HasActivePlayers;
			conversationInfo.IsOnHold = CustomerSession.OnHold;
			conversationInfo.Length = (System.DateTime.Now - customerSession.StartTime).Ticks;
			conversationInfo.Guid = customerSession.ConversationGuid;

			// monitoring
			EFS.RC.CC.Sessions.OfficeService.Operators.Supervisors.Monitoring supervisorMonitoringSession = this.supervisorMonitoringSession;
			if (null != supervisorMonitoringSession)
			{
				// set monitoring status
				switch (supervisorMonitoringSession.CurrentMonitoringStatus)
				{
					case EFS.RC.CC.Sessions.OfficeService.Operators.Supervisors.Monitoring.MonitoringStatus.Listen: conversationInfo.Monitoring = Messages.Communicator.Lync.Conversation.Info.MonitoringStatus.SupervisorListening; break;
					case EFS.RC.CC.Sessions.OfficeService.Operators.Supervisors.Monitoring.MonitoringStatus.Whisper: conversationInfo.Monitoring = Messages.Communicator.Lync.Conversation.Info.MonitoringStatus.SupervisorWhispering; break;
					case EFS.RC.CC.Sessions.OfficeService.Operators.Supervisors.Monitoring.MonitoringStatus.BargeIn: conversationInfo.Monitoring = Messages.Communicator.Lync.Conversation.Info.MonitoringStatus.SupervisorBargedIn; break;
					default: conversationInfo.Monitoring = Messages.Communicator.Lync.Conversation.Info.MonitoringStatus.None; break;
				}
			}

			// check recorder paused
			EFS.RC.CC.Tools.Recorders.Recorder recorder = this.recorder;
			System.DateTime? recorderPausedSince = (null != recorder && recorder.IsPaused) ? recorder.PausedSice : null;
			if (null != recorderPausedSince)
			{
				// set recorder paused seconds
				conversationInfo.RecorderPauseLength = (System.DateTime.Now - recorderPausedSince.Value).Ticks;
			}

			// response groups
			lock (syncAccess)
			{
				// get first active group
				foreach (EFS.RC.CC.Tools.Hunters.Group responseGroup in responseGroups)
				{
					if (responseGroup.Status == Tools.Hunters.Group.SessionStatus.Started)
					{
						// set monitoring status
						conversationInfo.HuntGroupSip = responseGroup.Sip;
						break;
					}
				}
			}

			// send this info
			return conversationInfo;
		}

		/// <summary>
		/// Stop all players
		/// </summary>
		public void StopAllPlayers()
		{
			// response groups
			lock (syncAccess)
			{
				// stop for customer
				customerSession.BeginStopPlayers();

				// stop for agents
				foreach (EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession in operatorSessions)
				{
					operatorSession.BeginStopPlayer();
				}
			}
		}

		/// <summary>
		/// Stop all players
		/// </summary>
		public void StopOperatorsPlayers()
		{
			lock (syncAccess)
			{
				// stop for agents
				foreach (EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession in operatorSessions)
				{
					operatorSession.BeginStopPlayer();
				}
			}
		}

		/// <summary>
		/// Stop all players
		/// </summary>
		public void StartOperatorsDialPlayer()
		{
			lock (syncAccess)
			{
				// start for agents
				foreach (EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession in operatorSessions)
				{
					operatorSession.BeginStartDialPlayer();
				}
			}
		}

		/// <summary>
		/// Stop all players
		/// </summary>
		public void StartOperatorsBusyPlayer()
		{
			lock (syncAccess)
			{
				// start for agents
				foreach (EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession in operatorSessions)
				{
					operatorSession.BeginStartBusyPlayer();
				}
			}
		}

		/// <summary>
		/// Start dial error plaier 
		/// </summary>
		public void StartOperatorsDialErrorPlayer()
		{
			lock (syncAccess)
			{
				// start for agents
				foreach (EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession in operatorSessions)
				{
					operatorSession.BeginStartDialErrorPlayer();
				}
			}
		}

		/// <summary>
		/// Execute delegate safe
		/// </summary>
		/// <param name="processWork"></param>
		public void ExecSafeProcess(ProcessWork processWork)
		{
			// check
			EFS.Common.Verify.Reference(processWork, "processWork");

			// lock and call outside work
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				processWork();
			}
		}

		/// <summary>
		/// Transfer
		/// </summary>
		/// <param name="inboundAgentAudioVideoCall"></param>
		internal void BeginTransfer(Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCall audioVideoCall, TransferFailed onTransferFailed)
		{
			// check
			EFS.Common.Verify.Reference(audioVideoCall, "audioVideoCall");
			EFS.Common.Verify.Reference(onTransferFailed, "onTransferFailed");

			// options
			Microsoft.Rtc.Collaboration.McuTransferOptions transferOption = new Microsoft.Rtc.Collaboration.McuTransferOptions();
			transferOption.PrivateAssistantDisabled = true;

			// transfer
			conversation.ConferenceSession.AudioVideoMcuSession.BeginTransfer(
				audioVideoCall,
				transferOption,
				EFS.Common.Async.AsyncResult.StateDelegate.End,
				EFS.Common.Async.AsyncResult.StateDelegate.State(EndTransfer, onTransferFailed));
		}

		/// <summary>
		/// Transfer
		/// </summary>
		/// <param name="inboundAgentAudioVideoCall"></param>
		internal void BeginDialOut(System.String uri, DialOutFailed onDialFailed)
		{
			// check
			EFS.Common.Verify.String(uri, "uri");
			EFS.Common.Verify.Reference(onDialFailed, "onDialFailed");

			// options
			Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoMcuDialOutOptions options = new Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoMcuDialOutOptions();
			options.PrivateAssistantDisabled = true;
			options.ParticipantDisplayName = CustomerSession.OfficeServiceConference.CustomerSession.DisplayName;
			options.ParticipantUri = uri;
			options.Media.Add(new Microsoft.Rtc.Collaboration.McuMediaChannel(Microsoft.Rtc.Collaboration.MediaType.Audio, Microsoft.Rtc.Collaboration.McuMediaChannelStatus.SendReceive));

			// dial out to uri
			conversation.ConferenceSession.AudioVideoMcuSession.BeginDialOut(
				uri,
				options,
				EFS.Common.Async.AsyncResult.StateDelegate.End,
				EFS.Common.Async.AsyncResult.StateDelegate.State(EndDialOut, onDialFailed));
		}

		#endregion

		#region Startup
		/// <summary>
		/// Startup, called asynk
		/// </summary>
		private void Startup()
		{
			// log
			EFS.EventLog.Log.LogInfo("Startup", this);

			// lock access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				if (status != OfficeServiceStatus.Idle)
				{
					throw new System.InvalidOperationException("Only Idle OfficeService can be started");
				}

				// started
				status = OfficeServiceStatus.Started;
			}

			// register termination
			customerSession.AsinkTerminatedDelegate.Register(OnCustomerSessionTerminated);

			// schedule
			BeginAsyncProcess2(ScheduleConference);
		}

		/// <summary>
		/// Schedule conference
		/// </summary>
		private void ScheduleConference()
		{
			// log
			EFS.EventLog.Log.LogInfo("ScheduleConference", this);

			// lock access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (Status != OfficeServiceStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// Init conference
				Microsoft.Rtc.Collaboration.ConferenceManagement.ConferenceScheduleInformation conferenceScheduleInfo = new Microsoft.Rtc.Collaboration.ConferenceManagement.ConferenceScheduleInformation();
				conferenceScheduleInfo.AutomaticLeaderAssignment = Microsoft.Rtc.Collaboration.AutomaticLeaderAssignment.SameEnterprise;
				conferenceScheduleInfo.LobbyBypass = Microsoft.Rtc.Collaboration.LobbyBypass.Disabled;
				conferenceScheduleInfo.AccessLevel = Microsoft.Rtc.Collaboration.ConferenceAccessLevel.Everyone;
				conferenceScheduleInfo.PhoneAccessEnabled = true;
				conferenceScheduleInfo.Mcus.Add(new Microsoft.Rtc.Collaboration.ConferenceManagement.ConferenceMcuInformation(Microsoft.Rtc.Collaboration.ConferenceManagement.McuType.AudioVideo));

				// schedule the conference
				customerSession.CustomersOfficeServiceEndPointApplication.CollaborationApplicationEndpoint.ConferenceServices.BeginScheduleConference(
					conferenceScheduleInfo,
					EFS.Common.Async.AsyncResult.StateDelegate.End,
					EFS.Common.Async.AsyncResult.StateDelegate.State(EndScheduleConference));
			}
		}

		/// <summary>
		/// End Schedule Conference
		/// </summary>
		/// <param name="ar"></param>
		private void EndScheduleConference(System.IAsyncResult ar)
		{
			// log
			EFS.EventLog.Log.LogInfo("EndScheduleConference", this);

			try
			{
				// conference is a run-time variable reference
				// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
				lock (syncAccess)
				{
					// end
					conference = customerSession.CustomersOfficeServiceEndPointApplication.CollaborationApplicationEndpoint.ConferenceServices.EndScheduleConference(ar);
				}

				// Join
				BeginAsyncProcess2(JoinOfficeConference);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				// we must shut down
				BeginShutdown();
			}
		}

		/// <summary>
		/// Join office conference
		/// </summary>
		private void JoinOfficeConference()
		{
			// log
			EFS.EventLog.Log.LogInfo("JoinOfficeConference", this);

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(customerSession.ConversationGuid, "JoinOfficeConference", this);

			// get end point
			Microsoft.Rtc.Collaboration.ApplicationEndpoint applicationEndpoint = customerSession.CustomersOfficeServiceEndPointApplication.CollaborationApplicationEndpoint;

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (Status != OfficeServiceStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// impersonate
				// uri
				System.String inpersonateUri = System.String.Format("sip:Office.Service@{0}", applicationEndpoint.DefaultDomain);
				conversation.Impersonate(inpersonateUri, null, "Office Service");

				// log impersonation
				EFS.EventLog.Log.LogInfo(System.String.Format("Impersonate conversation {0}", inpersonateUri), this);

				// set our events
				conversation.ConferenceSession.AudioVideoMcuSession.StateChanged += OnAudioVideoMcuSessionStateChanged;
				conversation.ConferenceSession.AudioVideoMcuSession.ParticipantEndpointAttendanceChanged += OnParticipantEndpointAttendanceChanged;
				conversation.StateChanged += OnConversationStateChanged;

				// conference options
				Microsoft.Rtc.Collaboration.ConferenceJoinOptions conferenceJoinOptions = new Microsoft.Rtc.Collaboration.ConferenceJoinOptions();
				conferenceJoinOptions.JoinMode = Microsoft.Rtc.Collaboration.JoinMode.TrustedParticipant;

				// Join
				conversation.ConferenceSession.BeginJoin(
					conference.ConferenceUri,
					conferenceJoinOptions,
					EFS.Common.Async.AsyncResult.StateDelegate.End,
					EFS.Common.Async.AsyncResult.StateDelegate.State(EndJoinOfficeConference));
			}
		}


		/// <summary>
		/// End Join Conference
		/// </summary>
		/// <param name="ar"></param>
		private void EndJoinOfficeConference(System.IAsyncResult ar)
		{
			// log
			EFS.EventLog.Log.LogInfo("EndJoinOfficeConference", this);

			try
			{
				// end join
				conversation.ConferenceSession.EndJoin(ar);

				// start recorder
				BeginAsyncProcess2(StartConferenceRecorder);

				// notify all up
				AsinkEstablishedDelegate.Start();
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				// we must shut down
				BeginShutdown();

				// exit
				return;
			}
		}

		/// <summary>
		/// Join office conference
		/// </summary>
		private void StartConferenceRecorder()
		{
			// log
			EFS.EventLog.Log.LogInfo("JoinOfficeConference", this);

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(customerSession.ConversationGuid, "JoinOfficeConference", this);

			// get end point
			Microsoft.Rtc.Collaboration.ApplicationEndpoint applicationEndpoint = customerSession.CustomersOfficeServiceEndPointApplication.CollaborationApplicationEndpoint;

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (Status != OfficeServiceStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// start recorder
				recorder = new EFS.RC.CC.Tools.Recorders.Recorder(this);
				recorder.AsinkTerminatedDelegate.Register(OnRecorderTerminated);
				recorder.BeginStartup();
			}
		}

		/// <summary>
		/// Start-up complete
		/// </summary>
		/// <param name="ar"></param>
		private void StartupComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("StartupComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
				EFS.EventLog.Log.LogInfo("StartupComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				// we must shut down
				BeginShutdown();
			}
		}

		#endregion

		#region Runtime

		/// <summary>
		/// Unhandled exception
		/// </summary>
		/// <param name="ex"></param>
		protected override void OnException(System.Exception ex)
		{
			// check
			EFS.Common.Verify.Reference(ex, "ex");

			// log
			EFS.EventLog.Log.LogException(ex, this);

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(null, "OnException", ex, this);

			// shut down
			BeginShutdown();
		}

		/// <summary>
		/// End transfer 
		/// </summary>
		/// <param name="result"></param>
		private void EndTransfer(System.IAsyncResult ar)
		{
			EFS.EventLog.Log.LogInfo("EndTransfer", this);

			try
			{
				// check
				conversation.ConferenceSession.AudioVideoMcuSession.EndTransfer(ar);
			}
			catch (System.Exception ex0)
			{
				// log
				EFS.EventLog.Log.LogException(ex0, this);

				// notify transfer failed
				try
				{
					// state delegate
					EFS.Common.Async.AsyncResult.StateDelegate stateDelegate = ar.AsyncState as EFS.Common.Async.AsyncResult.StateDelegate;
					EFS.Common.Verify.Reference(stateDelegate, "stateDelegate");

					// notify failed transfer
					TransferFailed onTransferFailed = stateDelegate.PassTrough as TransferFailed;
					EFS.Common.Verify.Reference(onTransferFailed, "onTransferFailed");

					// call
					onTransferFailed(ex0);
				}
				catch (System.Exception ex1)
				{
					// handle ex
					HandleExceptionSafe(ex1);
				}
			}
		}

		/// <summary>
		/// End dial out 
		/// </summary>
		/// <param name="result"></param>
		private void EndDialOut(System.IAsyncResult ar)
		{
			EFS.EventLog.Log.LogInfo("EndDialOut", this);

			try
			{
				// check
				conversation.ConferenceSession.AudioVideoMcuSession.EndDialOut(ar);
			}
			catch (System.Exception ex0)
			{
				// log
				EFS.EventLog.Log.LogException(ex0, this);

				// notify transfer failed
				try
				{

					// state delegate
					EFS.Common.Async.AsyncResult.StateDelegate stateDelegate = ar.AsyncState as EFS.Common.Async.AsyncResult.StateDelegate;
					EFS.Common.Verify.Reference(stateDelegate, "stateDelegate");

					// notify failed transfer
					DialOutFailed onDialOutFailed = stateDelegate.PassTrough as DialOutFailed;
					EFS.Common.Verify.Reference(onDialOutFailed, "onDialOutFailed");

					// call
					onDialOutFailed(ex0);
				}
				catch (System.Exception ex1)
				{
					// handle ex
					HandleExceptionSafe(ex1);
				}
			}
		}

		/// <summary>
		/// Context message received
		/// </summary>
		/// <param name="message"></param>
		private void InviteHuntGroupTerminated(EFS.RC.CC.Tools.Hunters.Group responseGroup)
		{
			// check
			EFS.Common.Verify.Reference(responseGroup, "responseGroup");

			// lock local acess
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check 
				if (responseGroups.Contains(responseGroup))
				{
					// remove
					responseGroups.Remove(responseGroup);
				}

				// check if any left, if none, stop all players for operators
				if (0 == responseGroups.Count)
				{
					StopOperatorsPlayers();
				}
			}
		}

		/// <summary>
		/// MCU state changed
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnAudioVideoMcuSessionStateChanged(object sender, Microsoft.Rtc.Signaling.StateChangedEventArgs<Microsoft.Rtc.Collaboration.McuSessionState> e)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnAudioVideoMcuSessionStateChanged", this);

			try
			{
				// check terminated
				if (e.State == Microsoft.Rtc.Collaboration.McuSessionState.Terminated)
				{
					BeginShutdown();
				}
			}
			catch (System.Exception ex)
			{
				// handle ex
				HandleExceptionSafe(ex);
			}
		}

		/// <summary>
		/// Attendance changed
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		private void OnParticipantEndpointAttendanceChanged(object sender, Microsoft.Rtc.Collaboration.ParticipantEndpointAttendanceChangedEventArgs<Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoMcuParticipantEndpointProperties> args)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnParticipantEndpointAttendanceChanged", this);

			try
			{
				// see all joined
				foreach (var kvp in args.Joined)
				{
					// check if it is us
					if (kvp.Key.Participant.Uri == conversation.LocalParticipant.Uri)
					{
						continue;
					}

					// check customer
					if (IsCustomer(kvp.Key.Participant.Uri))
					{
						// handle end point added
						customerSession.HandleParticipantEndPointAdded(kvp.Key);
					}
					else if (IsSupervisor(kvp.Key.Participant.Uri))
					{
						// get
						EFS.RC.CC.Sessions.OfficeService.Operators.Supervisors.Monitoring supervisorMonitoringSession = this.supervisorMonitoringSession;
						if (null != supervisorMonitoringSession)
						{
							supervisorMonitoringSession.HandleParticipantEndPointAdded(kvp.Key);
						}
					}
					else
					{
						// get session
						EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession = GetOperatorSession(kvp.Key.Participant.Uri);
						if (null != operatorSession)
						{
							operatorSession.HandleParticipantEndPointAdded(kvp.Key);
						}
						else
						{
							if (kvp.Key.EndpointType == Microsoft.Rtc.Collaboration.EndpointType.User)
							{
								UnknownConversationParticipantAdded(kvp.Key);
							}
						}
					}
				}

				// see all left
				foreach (var kvp in args.Left)
				{
					// check if it is us
					if (kvp.Key.Participant.Uri == conversation.LocalParticipant.Uri)
					{
						continue;
					}

					// check customer
					if (IsCustomer(kvp.Key.Participant.Uri))
					{
						// handle end point added
						customerSession.HandleParticipantEndPointRemoved(kvp.Key);
					}
					else if (IsSupervisor(kvp.Key.Participant.Uri))
					{
						// get
						EFS.RC.CC.Sessions.OfficeService.Operators.Supervisors.Monitoring supervisorMonitoringSession = this.supervisorMonitoringSession;
						if (null != supervisorMonitoringSession)
						{
							supervisorMonitoringSession.HandleParticipantEndPointRemoved(kvp.Key);
						}
					}
					else
					{
						// get session
						EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession = GetOperatorSession(kvp.Key.Participant.Uri);
						if (null != operatorSession)
						{
							operatorSession.HandleParticipantEndPointRemoved(kvp.Key);
						}
					}
				}
			}
			catch (System.Exception ex)
			{
				// log
				HandleExceptionSafe(ex);
			}
		}

		/// <summary>
		/// Added conversation participant
		/// </summary>
		/// <param name="conversationParticipant"></param>
		private void UnknownConversationParticipantAdded(Microsoft.Rtc.Collaboration.ParticipantEndpoint conversationParticipantEndPoint)
		{
			// check
			EFS.Common.Verify.Reference(conversationParticipantEndPoint, "conversationParticipantEndPoint");

			// log
			EFS.EventLog.Log.LogInfo("UnknownConversationParticipantAdded", this);
			try
			{
				// get app
				EFS.RC.CC.Applications.EndPoints.OfficeService.Operators operatorsOfficeServiceEndPointApplication = CustomerSession.
					CustomersOfficeServiceEndPointApplication.
					ApplicationPresence.
					EndPointsPresencesRegistrar.
					OperatorOfficeServiceEndPointPresence.
					PresenceEndPointApplication;

				// get agent for an external invite
				EFS.RC.CC.Presences.Operators.Presence presence = operatorsOfficeServiceEndPointApplication.OperatorsTeam.GetAgentBySIP(conversationParticipantEndPoint.Participant.Uri);

				// check
				if (null == presence)
				{
					// make anon operator
					presence = new EFS.RC.CC.Presences.Operators.Anonymous(conversationParticipantEndPoint.Participant.Uri);
				}

				// make a invited agent session
				EFS.RC.CC.Sessions.OfficeService.Operators.Invited operatorSession = new EFS.RC.CC.Sessions.OfficeService.Operators.Invited(presence, customerSession);

				// register for established
				operatorSession.AsinkEstablishedDelegate.Register(InvitedOperatorEstablished);

				// start agent session
				operatorSession.BeginStartup();
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				// remove
				TryEjectParticipant(conversationParticipantEndPoint.Participant.Uri);
			}
		}

		/// <summary>
		/// Invited operator established
		/// </summary>
		/// <param name="invitedOperatorSession"></param>
		private void InvitedOperatorEstablished(EFS.RC.CC.Sessions.OfficeService.IParticipant officeServiceSessionParticipant)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnSessionTerminated", this);

			// check
			EFS.Common.Verify.Reference(officeServiceSessionParticipant, "officeServiceSessionParticipant");

			// cast
			EFS.RC.CC.Sessions.OfficeService.Operators.Invited invitedOperatorSession = officeServiceSessionParticipant as EFS.RC.CC.Sessions.OfficeService.Operators.Invited;
		
			// check
			EFS.Common.Verify.Reference(invitedOperatorSession, "invitedOperatorSession");

			// set en point 
			invitedOperatorSession.HandleParticipantEndPointAdded(GetEndPoint(invitedOperatorSession.OperatorPresence.User.SIP));
		}

		/// <summary>
		/// Conversation events
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnConversationStateChanged(object sender, Microsoft.Rtc.Signaling.StateChangedEventArgs<Microsoft.Rtc.Collaboration.ConversationState> e)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnConversationStateChanged", this);

			try
			{
				// check state
				if (e.State == Microsoft.Rtc.Collaboration.ConversationState.Terminating)
				{
					// shut down
					BeginShutdown();
				}
			}
			catch (System.Exception ex)
			{
				// handle ex
				HandleExceptionSafe(ex);
			}
		}

		/// <summary>
		/// Supervisor session terminated
		/// </summary>
		/// <param name="supervisorMonitoringSession"></param>
		private void OnSupervisorSessionTerminated(EFS.RC.CC.Sessions.OfficeService.IParticipant officeServiceSessionParticipant)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnSupervisorSessionTerminated", this);

			// check
			EFS.Common.Verify.Reference(officeServiceSessionParticipant, "officeServiceSessionParticipant");

			// cast
			EFS.RC.CC.Sessions.OfficeService.Operators.Supervisors.Monitoring supervisorMonitoringSession = officeServiceSessionParticipant as EFS.RC.CC.Sessions.OfficeService.Operators.Supervisors.Monitoring;

			// check
			EFS.Common.Verify.Reference(supervisorMonitoringSession, "supervisorMonitoringSession");

			// sync access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check if we have any and is the same
				if (null != this.supervisorMonitoringSession && this.supervisorMonitoringSession.Equals(supervisorMonitoringSession))
				{
					// eject if still here
					TryEjectParticipant(this.supervisorMonitoringSession.SupervisorPresence.User.SIP);

					// remove reference
					this.supervisorMonitoringSession = null;

					// check sanity
					BeginAsyncProcess2(CheckConferenceSantity);
				}
			}
		}

		/// <summary>
		/// StepByStep session terminated
		/// </summary>
		/// <param name="stepByStepAVRSession"></param>
		private void OnStepByStepSessionTerminated(EFS.RC.CC.Tools.AVR.AStepByStep stepByStepAVRSession)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnStepByStepSessionTerminated", this);

			// check
			EFS.Common.Verify.Reference(stepByStepAVRSession, "stepByStepAVRSession");

			// sync access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check if we have any and is the same
				if (null != this.stepByStepAVRSession && this.stepByStepAVRSession.Equals(stepByStepAVRSession))
				{
					// remove reference
					this.stepByStepAVRSession = null;

					// check sanity
					BeginAsyncProcess2(CheckConferenceSantity);
				}
			}
		}

		/// <summary>
		/// Supervisor session terminated
		/// </summary>
		/// <param name="supervisorMonitoringSession"></param>
		private void OnOperatorSessionTerminated(EFS.RC.CC.Sessions.OfficeService.IParticipant officeServiceSessionParticipant)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnOperatorSessionTerminated", this);

			// check
			EFS.Common.Verify.Reference(officeServiceSessionParticipant, "officeServiceSessionParticipant");

			// cast
			EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession = officeServiceSessionParticipant as EFS.RC.CC.Sessions.OfficeService.Operators.Operator;

			// check
			EFS.Common.Verify.Reference(operatorSession, "operatorSession");

			// sync access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check if we have any and is the same
				if (!operatorSessions.Contains(operatorSession))
				{
					throw new System.InvalidOperationException("Terminated Operator Session is not registerd");
				}

				// eject if still here
				TryEjectParticipant(operatorSession.OperatorPresence.User.SIP);

				// remove reference
				operatorSessions.Remove(operatorSession);

				// check sanity
				BeginAsyncProcess2(CheckConferenceSantity);
			}
		}

		/// <summary>
		/// Check Sanity
		/// </summary>
		private void CheckConferenceSantity()
		{
			// log
			EFS.EventLog.Log.LogInfo("CheckConferenceSantity", this);

			// sync access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// we were not hunting, check if anyone here but the customer 
				if (0 == operatorSessions.Count)
				{
					// check AVR
					if (null != stepByStepAVRSession)
					{
						return;
					}

					// check if supervisor barged in
					if (null != supervisorMonitoringSession && supervisorMonitoringSession.CurrentMonitoringStatus == Sessions.OfficeService.Operators.Supervisors.Monitoring.MonitoringStatus.BargeIn)
					{
						// no operators but supervizor is barged in
						return;
					}

					// check if customer session is hunted
					if (customerSession is EFS.RC.CC.Sessions.OfficeService.Customers.HuntQueue.DialIn)
					{
						// check if customer is in hunt process
						if (!(customerSession as EFS.RC.CC.Sessions.OfficeService.Customers.HuntQueue.DialIn).IsHunted)
						{
							BeginShutdown();
						}
					}
					else
					{
						BeginShutdown();
					}
				}
				else
				{
					// check if the conversation was transferred and is such case, if on hold resume
					if (customerSession.OnHold)
					{
						// stop any music
						StopAllPlayers();

						// resume
						customerSession.Resume();
					}
				}
			}
		}

		/// <summary>
		/// allocated event
		/// </summary>
		/// <param name="agent"></param>
		private void OnCustomerSessionTerminated(EFS.RC.CC.Sessions.OfficeService.IParticipant officeServiceSessionParticipant)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnCustomerSessionTerminated", this);

			// check
			EFS.Common.Verify.Reference(officeServiceSessionParticipant, "officeServiceSessionParticipant");

			// cast
			EFS.RC.CC.Sessions.OfficeService.Customers.Customer customerSession = officeServiceSessionParticipant as EFS.RC.CC.Sessions.OfficeService.Customers.Customer;

			// check
			EFS.Common.Verify.Reference(customerSession, "customerSession");

			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check is expected
				if (null == this.customerSession || !this.customerSession.Equals(customerSession))
				{
					throw new System.InvalidOperationException("Terminated Customer Session is different");
				}

				// check if we have any agents
				if (0 == operatorSessions.Count)
				{
					BeginShutdown();
				}
				else
				{
					// check number of operators left in conversation
					if (this.customerSession is EFS.RC.CC.Sessions.OfficeService.Customers.HuntQueue.DialIn)
					{
						// if started close
						BeginAsyncProcess2(DelayCloseConference);
					}
					else
					{
						BeginShutdown();
					}
				}
			}
		}

		/// <summary>
		/// Start close
		/// </summary>
		private void DelayCloseConference()
		{
			lock (syncAccess)
			{
				// if started close
				if (status == Confereces.OfficeService.OfficeServiceStatus.Started)
				{
					// start time-out shut down tool for conference
					EFS.RC.CC.Tools.Schedulers.OfficeConferenceWrap officeConferenceWrap = new EFS.RC.CC.Tools.Schedulers.OfficeConferenceWrap(this);
					officeConferenceWrap.Execute();
				}
			}
		}

		/// <summary>
		/// Recorder terminated
		/// </summary>
		/// <param name="player"></param>
		private void OnRecorderTerminated(EFS.RC.CC.Tools.Recorders.Recorder recorder)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnRecorderTerminated", this);

			// check
			EFS.Common.Verify.Reference(recorder, "recorder");

			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check
				if (null != this.recorder && recorder.Equals(recorder))
				{
					// remove
					this.recorder = null;
				}
			}
		}

		/// <summary>
		/// Eject participant
		/// </summary>
		/// <param name="agent"></param>
		private void TryEjectParticipant(System.String participantUri)
		{
			// log
			EFS.EventLog.Log.LogInfo("EjectParticipant", this);

			// check
			EFS.Common.Verify.String(participantUri, "participantUri");

			try
			{
				// get it
				Microsoft.Rtc.Collaboration.ParticipantEndpoint participantEndpoint = GetEndPoint(participantUri);

				// check is still in
				if ((null != participantEndpoint) && (conversation.State == Microsoft.Rtc.Collaboration.ConversationState.Conferenced))
				{
					conversation.ConferenceSession.BeginEject(
						participantEndpoint.Participant,
						EFS.Common.Async.AsyncResult.StateDelegate.End,
						EFS.Common.Async.AsyncResult.StateDelegate.State(EndEjectParticipant));
				}
			}
			catch { }
		}

		/// <summary>
		/// End modify role request
		/// </summary>
		/// <param name="ar"></param>
		private void EndEjectParticipant(System.IAsyncResult ar)
		{
			// log
			EFS.EventLog.Log.LogInfo("EndEjectParticipant", this);

			try
			{
				// end
				conversation.ConferenceSession.EndEject(ar);
			}
			catch { }
		}

		/// <summary>
		/// Pause
		/// </summary>
		private void PauseRecorder()
		{
			// log
			EFS.EventLog.Log.LogInfo("Pause", this);

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != OfficeServiceStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// check
				if (null == recorder)
				{
					throw new System.InvalidOperationException("Recorder not Initialized");
				}

				// pause
				recorder.BeginPause();
			}
		}

		/// <summary>
		/// Hold
		/// </summary>
		/// <returns></returns>
		private void HoldCustomerConversation()
		{
			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != OfficeServiceStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// hold supervisor
				if (null != supervisorMonitoringSession)
				{
					// hold supervisor
					supervisorMonitoringSession.BeginHold();
				}

				// hold
				customerSession.Hold();
			}
		}

		/// <summary>
		/// Resume
		/// </summary>
		/// <returns></returns>
		private void ResumeCustomerConversation()
		{
			// lock
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != OfficeServiceStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// hold
				customerSession.Resume();
			}
		}


		/// <summary>
		/// Resume
		/// </summary>
		private void ResumeRecorder()
		{
			// log
			EFS.EventLog.Log.LogInfo("Resume", this);

			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check status
				if (status != OfficeServiceStatus.Started)
				{
					throw new System.InvalidOperationException("Invalid Status for current operation. Expected Status: Started");
				}

				// check
				if (null == recorder)
				{
					throw new System.InvalidOperationException("Recorder not Initialized");
				}

				// resume
				recorder.BeginResume();
			}
		}

		#endregion

		#region Shutdown

		/// <summary>
		/// Stop all players
		/// </summary>
		private void StopAllOperators()
		{
			// response groups
			lock (syncAccess)
			{
				// agents 
				foreach (EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession in operatorSessions)
				{
					operatorSession.BeginShutdown();
				}

				// supervisor
				if (null != supervisorMonitoringSession)
				{
					// close supervisor session
					supervisorMonitoringSession.BeginShutdown();
				}
			}
		}

		/// <summary>
		/// Wrap
		/// </summary>
		private void Wrap()
		{
			// log
			EFS.EventLog.Log.LogInfo("Wrap", this);

			// lock local acess
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// agents 
				foreach (EFS.RC.CC.Sessions.OfficeService.Operators.Operator operatorSession in operatorSessions)
				{
					operatorSession.BeginWrap();
				}

				// supervisor
				if (null != supervisorMonitoringSession)
				{
					// close supervisor session
					supervisorMonitoringSession.BeginWrap();
				}
			}
		}

		/// <summary>
		/// Sht down
		/// </summary>
		private void Shutdown()
		{
			// log
			EFS.EventLog.Log.LogInfo("Shutdown", this);

			// lock local acess
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check already terminated
				if (status != OfficeServiceStatus.Terminated)
				{
					// set terinated
					status = OfficeServiceStatus.Terminated;

					// notify terminated
					AsinkTerminatedDelegate.Start();
				}
				else
				{
					// already terminated
					return;
				}
			}

			// stop operators and 
			StopAllOperators();

			// stop music
			StopAllPlayers();

			// check if not inviting a hunt group
			StopResponseGroupInvite();

			// customer session
			customerSession.BeginShutdown();

			// make termination
			EFS.RC.CC.Tools.Functions.Termination termination = new EFS.RC.CC.Tools.Functions.Termination(this);

			// terminate all
			termination.TerminateConference(conversation, EndTerminateConference);
		}

		/// <summary>
		/// End terminate conference
		/// </summary>
		/// <param name="ar"></param>
		private void EndTerminateConference(System.IAsyncResult ar)
		{
			// log
			EFS.EventLog.Log.LogInfo("EndTerminateConference", this);

			try
			{
				// remove delegates
				conversation.ConferenceSession.AudioVideoMcuSession.StateChanged -= OnAudioVideoMcuSessionStateChanged;
				conversation.ConferenceSession.AudioVideoMcuSession.ParticipantEndpointAttendanceChanged -= OnParticipantEndpointAttendanceChanged;

				// end conference
				conversation.ConferenceSession.EndTerminateConference(ar);

				// make termination
				EFS.RC.CC.Tools.Functions.Termination termination = new EFS.RC.CC.Tools.Functions.Termination(this);
				termination.Terminate(conversation);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		/// <summary>
		/// End terminate conversation
		/// </summary>
		/// <param name="ar"></param>
		private void EndTerminateConversation(System.IAsyncResult ar)
		{
			// log
			EFS.EventLog.Log.LogInfo("EndTerminateConversation", this);

			try
			{
				// remove delegate
				conversation.StateChanged -= OnConversationStateChanged;

				// end terminate
				conversation.EndTerminate(ar);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		/// <summary>
		/// Shut down complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnShutdownComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnShutdownComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
				EFS.EventLog.Log.LogInfo("OnShutdownComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		#endregion // shutdown
	}
}